package fpm;

import auth.FuncPermission;
import frame.service.context.ServiceContext;
import frame.service.standard.Constants;
import frame.service.standard.MiayFilter;
import frame.service.standard.MiayRuntimeException;
import frame.service.util.FrameUtil;
import http.RedisSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.ClassPathResource;
import page.Pages;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class PageServiceFilter implements MiayFilter, InitializingBean {
    private Logger logger = LoggerFactory.getLogger(PageServiceFilter.class);
    /**
     * pages.json映射类
     * */
    private Pages pages;

    /**
     * key: request url
     * val: 支持的业务操作场景
     * */
    private Map<String, List<String>> serviceMap = new HashMap<>(4);

    /**
     * 服务链信息汇集
     * key: sceneId
     * val: requestChain
     * */
    private Map<String, List<String>> requestChainInfo = new HashMap<>(4);

    @Override
    public void doPass(ServiceContext context) {
        //访问的服务场景值
        String sceneId = context.getReqContentString(Constants.SCENE_ID);
        //访问的服务url
        String serviceId = generateServiceId(context);
        logger.info(serviceId);

        if(sceneId == null){
            if(!pages.getCommon().contains(serviceId)){
                logger.error("930005");
                throw new MiayRuntimeException("930005");
            }
        }else{
            RedisSession redisSession = context.getRedisSession();
            //获取会话内权限信息
            FuncPermission funcPermission = redisSession.getSessionData(RedisSession.Constants.FUNC_PERMISSION, FuncPermission.class);
            if(funcPermission==null || funcPermission.getMenus()==null || funcPermission.getMenus().size()==0){
                logger.error("930008");
                throw new MiayRuntimeException("930008");
            }
            logger.info("checkPermission:{}|{}", sceneId, serviceId);
            if(funcPermission.checkPermission(sceneId)){
                //具备操作权限，则判断后台配置是否支持该业务操作访问该服务
                if(serviceMap.get(serviceId)==null || !serviceMap.get(serviceId).contains(sceneId)){
                    //当前业务操作无该服务访问权限
                    logger.error("930007");
                    throw new MiayRuntimeException("930007", new String[]{serviceId, sceneId});
                }else if(requestChainInfo.get(sceneId)!=null){
                    String rcId = context.getReqContentString(Constants.REQUEST_CHAIN_ID);
                    Map<String,Integer> sessionData = redisSession.getSessionData(RedisSession.Constants.REQUEST_CHAIN_LIMIT, Map.class);
                    if(sessionData!=null){
                        Integer nowStep = sessionData.get(sceneId + ":" + rcId);
                        if(nowStep==null && serviceId.equals(requestChainInfo.get(sceneId).get(0))){
                            sessionData.put(sceneId + ":" + rcId, 0);
                            redisSession.setSessionData(RedisSession.Constants.REQUEST_CHAIN_LIMIT, FrameUtil.obj2json(sessionData));
                        }else{
                            if(requestChainInfo.get(sceneId).indexOf(serviceId) - nowStep > 1){
                                logger.error("930010");
                                throw new MiayRuntimeException("930010");
                            }
                        }
                    }
                }
            }
        }
    }

    public String generateServiceId(ServiceContext context){
        String[] attr = new String[]{
                context.getReqContentString(Constants.REQUEST_SAFE_MODE),
                context.getReqContentString(Constants.REQUEST_MODULE),
                context.getReqContentString(Constants.REQUEST_SERVICE),
                context.getReqContentString(Constants.REQUEST_ACTION),
                context.getReqContentString(Constants.REQUEST_VERSION)
        };
        return FrameUtil.stringFormatter("/%s/%s/%s/%s-%s", attr);
    }

    @Override
    public boolean checkSuit(ServiceContext context) {
        return true;
    }

    @Override
    public int getOrder() {
        return 1;
    }

    private void commonServiceConf(Pages.Menu menu, String sceneId, Pages.Operation operation){
        if(menu.getServices()!=null){
            //增加通用级服务接口至业务操作内
            for(String m1CommonService: menu.getServices()){
                if(serviceMap.get(m1CommonService)!=null){
                    serviceMap.get(operation.getService()).add(sceneId);
                }else{
                    List<String> sceneList = new ArrayList<>();
                    sceneList.add(sceneId);
                    serviceMap.put(m1CommonService, sceneList);
                }
            }
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        //加载pages.json，用于权限校验
        ClassPathResource pagesConf = new ClassPathResource("pages.json");
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        byte[] tmp = new byte[1024];
        int len = -1;

        InputStream inputStream = pagesConf.getInputStream();
        while((len=inputStream.read(tmp))!=-1){
            bout.write(tmp, 0 , len);
        }
        inputStream.close();

        this.pages = FrameUtil.str2Obj(new String(bout.toByteArray()), Pages.class);

        List<Pages.MenuLevel1> menus = this.pages.getMenus();
        //一级菜单遍历
        for(Pages.MenuLevel1 menuLevel1: menus){
            List<Pages.MenuLevel2> menuLevel2s = menuLevel1.getMenus();
            //二级菜单遍历
            for(Pages.MenuLevel2 menuLevel2: menuLevel2s){
                List<Pages.Operation> operations = menuLevel2.getOperations();
                //业务操作遍历
                for(Pages.Operation operation: operations){
                    String sceneId = menuLevel1.getId()+":"+menuLevel2.getId()+":"+operation.getId();

                    commonServiceConf(menuLevel1, sceneId, operation);
                    commonServiceConf(menuLevel2, sceneId, operation);

                    if(operation.getService()!=null){
                        if(serviceMap.get(operation.getService())!=null){
                            serviceMap.get(operation.getService()).add(sceneId);
                        }else{
                            List<String> sceneList = new ArrayList<>();
                            sceneList.add(sceneId);
                            serviceMap.put(operation.getService(), sceneList);
                        }
                        continue;
                    }

                    if(operation.getRequestChain()!=null && operation.getRequestChain().size()!=0){
                        requestChainInfo.put(sceneId, operation.getRequestChain());
                        for(String request: operation.getRequestChain()){
                            if(serviceMap.get(request)!=null){
                                serviceMap.get(request).add(sceneId);
                            }else{
                                List<String> sceneList = new ArrayList<>();
                                sceneList.add(sceneId);
                                serviceMap.put(request, sceneList);
                            }
                        }
                    }
                }
            }
        }
    }
}
